/*
 * Copyright 2008-2021 tianzhu.cloud. All rights reserved.
 * Support: http://www.tianzhu.cloud
 * License: http://www.tianzhu.cloud/license
 */

package com.ruoyi.framework.config;

import com.alibaba.fastjson.JSON;
import com.ruoyi.framework.config.properties.ThreadPoolProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异步配置
 *
 * @author 挺好的 2023年02月27日 9:44
 */
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    /**
     * 线程池
     */
    @Resource (name = "threadPoolProperties")
    private ThreadPoolProperties threadPoolProperties;

    @Bean
    public Executor taskExecutor () {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(this.threadPoolProperties.getCoreSize());
        executor.setMaxPoolSize(this.threadPoolProperties.getMaxSize());
        executor.setQueueCapacity(this.threadPoolProperties.getQueueCapacity());
        executor.setThreadNamePrefix(this.threadPoolProperties.getThreadPoolNamePrefix());
        //        executor.setKeepAliveSeconds(this.threadPoolProperties.getKeepAliveSeconds());
        //设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Bean
    public TaskScheduler taskScheduler () {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(this.threadPoolProperties.getCoreSize());
        taskScheduler.initialize();
        return taskScheduler;
    }


    //这里的Executor 是ThreadPool 它会返回一个线程池
    //需要把这个注入到JavaBean
    @Bean
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);//线程池核心数量
        executor.setMaxPoolSize(30);//最大的线程池数量，也就超过核心池的时候，才会使用的
        executor.setQueueCapacity(60);//缓冲队列的个数
        executor.setKeepAliveSeconds(60);// 超出核心进程数量之外的线程数量的最大存活时间。默认60秒，超过则会被杀掉
        executor.setThreadNamePrefix("Caicai_Async_");//线程名的前缀

        executor.setWaitForTasksToCompleteOnShutdown(true);//是否等待所有线程执行完毕之后关闭线程池
        executor.setAwaitTerminationSeconds(60); // 等待的时间，默认是0.也就是setWaitForTasksToCompleteOnShutdown 这个的等待时间

        //拒绝策略
        //用与被拒绝处理的一个处理程序，它直接在executor 的方法的调用线程中运行被拒绝的任务
        executor.setRejectedExecutionHandler(
                new ThreadPoolExecutor.AbortPolicy()
                //AbortPolicy() 当线程池满了，队列也满了，它会直接废弃并且报出异常
                //CallerRunsPolicy() //如果执行程序已经关闭了，则会丢弃这个任务
                //DiscardOldestPolicy() 当线程数量等于最大线程数量，队列是满的情况下，它就会抛弃线程池种最后一个，并执行新传递进来的任务
                //DiscardPolicy()  当线程数量等于或最大线程数量，不做任何的操作
        );

        //线程初始化
        executor.initialize();
        return executor;
    }
    // getAsyncUncaughtExceptionHandler 定义异常处理
    /*
     * 定义异步任务异常处理类，只会处理没有返回结果异步任务，有返回结果的任务会
     * 交给客户端
     * */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }

    static class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler{
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            log.info("AsyncError:{} ,Method:{},Param:{}",throwable.getMessage(),method.getName(),
                    JSON.toJSONString(objects));//返回值，以json的形式返回
            throwable.printStackTrace();//打印
            //TODO 发送邮件或者手机短信

        }
    }
}
